﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace DX11Engine.Diagnostics
{
    public class RenderingDiagnostic
    {
        private Stopwatch _stpWtch = new Stopwatch();
        public Dictionary<string, StatisticSlice> TimingData = new Dictionary<string, StatisticSlice>();
        public Dictionary<string, StatisticSlice> SceneManagementData = new Dictionary<string, StatisticSlice>();
        private Dictionary<string, Stopwatch> _internlStpWtchs = new Dictionary<string, Stopwatch>();
        public int AvgSamples { get; set; }

        public RenderingDiagnostic() 
        { this.AvgSamples = 10; }

        public void BeginSpecific(string Name)
        {
            if (!_internlStpWtchs.ContainsKey(Name))
            { _internlStpWtchs.Add(Name, new Stopwatch()); }
            _internlStpWtchs[Name].Restart();
        }

        public void EndSpecific(string Name)
        {
            long Elpsed = _internlStpWtchs[Name].ElapsedTicks;
            _internlStpWtchs[Name].Stop();
            UpdateDiagnostics(Name, Elpsed);
        }

        public void BeginSingle()
        { _stpWtch.Restart(); }

        public void EndSingle(string Name)
        { 
            long Elpsed = _stpWtch.ElapsedTicks;
            UpdateDiagnostics(Name, Elpsed);
        }

        private void UpdateDiagnostics(string Name, long Time)
        {
            if (TimingData.ContainsKey(Name)) { TimingData[Name].ValueAverage = Time; }
            else { TimingData.Add(Name, new StatisticSlice() { ValueAverage = Time }); }
        }

        public void AddToStatistic(string Name, long Value)
        {
            if (SceneManagementData.ContainsKey(Name)) { SceneManagementData[Name].ValueAverage = Value; }
            else { SceneManagementData.Add(Name, new StatisticSlice() { ValueAverage = Value }); }
        }

        public void WritePerfLog(string FileName = "engineDiagnostics.txt")
        {
            List<string> OutputLog = new List<string>();
            OutputLog.Add("Log Time: " + DateTime.Now.ToString() + Environment.NewLine);
            OutputLog.Add("********************************************");
            OutputLog.Add("******* Scene Management Statistics: *******");
            OutputLog.Add("********************************************");
            if (SceneManagementData.Count > 0)
            {
                foreach (KeyValuePair<string, StatisticSlice> kvp in SceneManagementData)
                {
                    OutputLog.Add(Environment.NewLine + "---- \"" + kvp.Key + "\" ----");
                    OutputLog.Add("Average Value: " + kvp.Value.ValueAverage);
                    OutputLog.Add("Average Samples: " + kvp.Value.AvgSamples);
                    OutputLog.Add("Max Value: " + kvp.Value.ValueMax);
                    OutputLog.Add("Min Value: " + kvp.Value.ValueMin);
                }
            }

            OutputLog.Add(Environment.NewLine + "**********************************");
            OutputLog.Add("******* Timing Statistics: *******");
            OutputLog.Add("**********************************");
            if (SceneManagementData.Count > 0)
            {
                long frmRef = TimingData["FrameTime"].ValueAverage;
                long EntyPrcsngRef = TimingData["ProcessingEntity"].ValueAverage;
                Dictionary<string, decimal> PercentageValues = new Dictionary<string, decimal>();

                foreach (KeyValuePair<string, StatisticSlice> kvp in TimingData)
                {
                    OutputLog.Add(Environment.NewLine + "---- \"" + kvp.Key + "\" ----");
                    OutputLog.Add("Average Samples: " + kvp.Value.AvgSamples);
                    OutputLog.Add("Max Value: " + kvp.Value.ValueMax);
                    OutputLog.Add("Average Value: " + kvp.Value.ValueAverage + Reference(kvp.Value.ValueAverage, frmRef, EntyPrcsngRef));
                    OutputLog.Add("Min Value: " + kvp.Value.ValueMin + Reference(kvp.Value.ValueMin, frmRef, EntyPrcsngRef));

                    PercentageValues.Add(kvp.Key, ((decimal)kvp.Value.ValueAverage * (decimal)100 / (decimal)frmRef));
                }

                OutputLog.Add(Environment.NewLine + "******* Cost Per Operation Per Frame: *******");
                PercentageValues.OrderByDescending(v => v.Value).ToList().ForEach(kvp => OutputLog.Add(kvp.Key + ": " + kvp.Value.ToString("###.###") + "%" ));
            }
            System.IO.File.WriteAllLines(FileName, OutputLog.ToArray());
            System.Diagnostics.Process.Start(FileName);
        }

        private string Reference(long value, long FrameReference, long EntityProcessingReference)
        {
            if (FrameReference > 0 && EntityProcessingReference > 0)
            { 
                return " (" + ((value * 100) / FrameReference).ToString() + "% / avg. frame time; "
                    + ((value * 100) / EntityProcessingReference).ToString() + "% / avg entity time)";
            }
            return "";
        }
    }

    public class StatisticSlice
    {
        public StatisticSlice() {
            this.ValueMax = long.MinValue;
            this.ValueMin = long.MaxValue;
        }

        public long ValueMax { get; private set; }
        public long ValueMin { get; private set; }

        private long _avgSmpls = 10;
        public long AvgSamples 
        { 
            get { return _avgSmpls;}
            set { _avgSmpls = value; _avgValList = new long[value]; } 
        }

        private long[] _avgValList = new long[10];
        private long _avgValIndx = 0;
        public long ValueAverage
        {
            set {
                _avgValList[_avgValIndx++] = value;
                if (_avgValIndx > (AvgSamples - 1)) { _avgValIndx = 0; }
                if (ValueMax < value) { ValueMax = value; }
                if (ValueMin > value) { ValueMin = value; }
            }
            get {
                long TimeVal = 0;
                for (int i = 0; i < _avgValList.Length; i++)
                { TimeVal += _avgValList[_avgValIndx]; }
                return TimeVal / AvgSamples;
            }
        }
    }
}
